﻿using DotNetCommon.Extensions;
using System;
using System.Collections;
using System.Linq;

namespace DotNetCommon.Validate.Annotations;

/// <summary>
/// 方便对集合中的每个元素进行验证, 示例:
/// <code>
/// public class Person
/// {
///     [VEach(typeof(VNotNullOrWhiteSpaceAttribute))]
///     [VEach(typeof(VLengthInRangeAttribute), [2, 4])]
///     public List&lt;string> StudentNames { get; set; }
/// }
/// </code>
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public sealed class VEachAttribute(Type validAttr, object[] paras = null) : Attribute, IValidateAction
{
    public Type ValidAttr { get; } = validAttr;
    public object[] Paras { get; } = paras;

    public void Action(IValidateContext ctx)
    {
        if (ctx.StopValidate) return;
        if (ctx.ModelValue != null && ctx.ModelValue is IEnumerable t)
        {
            var reflect = ctx.ModelValue.GetType().GetClassGenericFullName();
            if (ValidAttr.IsAssignableTo(typeof(IValidateAction)))
            {
                //创建 IValidateAction 实例
                var validAttrCtor = ValidAttr.GetConstructors().First();
                var reqParas = validAttrCtor.GetParameters();
                var paras = Paras;
                if (reqParas.Length > paras.Length)
                {
                    //补充缺少的参数
                    var newParas = paras.ToList();
                    for (int i = paras.Length; i < reqParas.Length; i++)
                    {
                        if (reqParas[i].HasDefaultValue)
                        {
                            newParas.Add(reqParas[i].DefaultValue);
                        }
                        else
                        {
                            newParas.Add(reqParas[i].ParameterType.GetDefault());
                        }
                    }
                    paras = [.. newParas];
                }
                var validInst = validAttrCtor.Invoke(paras);
                var action = ValidAttr.GetMethod("Action");

                var type = typeof(ValidateContext<>).MakeGenericType(reflect.GenericTypes[0].type);
                var method = type.GetMethod("Action");
                //internal ValidateContext(T model, string modelName, IValidateContext parent)
                var ctor = type.GetConstructors(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).First(i => i.GetParameters().Length == 3);
                var idx = -1;
                foreach (var item in t)
                {
                    idx++;
                    var newCtx = ctor.Invoke([item, $"[{idx}]", ctx]);
                    action.Invoke(validInst, [newCtx]);
                }
            }
        }
    }
}
